package com.haotian.zip.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Deflater;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.log4j.Logger;

public class _ZipFileUtils {

	private static Logger LOGGER = Logger.getLogger(_ZipFileUtils.class);

	public static void main(String[] args) throws IOException {
		StopWatch sw = new StopWatch();
		sw.start();
		compressFiles2Zip(new File("c:/maven-repository"), new File("c://123.zip"));
		sw.stop();
		System.out.println(sw.getTime());
	}

	/**
	 * 压缩一个文件夹到zip
	 * <p>
	 * </p>
	 * 
	 * @param srcFile
	 * @param targetFile
	 * @throws IOException
	 */
	public static void compressFiles2Zip(File srcFile, File targetFile) throws IOException {
		// 待压缩文件集合
		List<File> fileList = new ArrayList<>();

		// 获取压缩文件夹的根目录
		String rootName = srcFile.getName();

		// 初始化文件集合
		initFileList(fileList, srcFile);

		// 开始压缩
		compressFiles2Zip(rootName, fileList, targetFile);
	}

	/*
	 * 递归将所有文件都加入到filiList当中
	 */
	private static void initFileList(List<File> fileList, File srcFile) {
		if (srcFile.isDirectory()) {
			fileList.add(srcFile);
			File[] filestmp = srcFile.listFiles();
			for (File file : filestmp) {
				initFileList(fileList, file);
			}
		} else {
			fileList.add(srcFile);
		}
	}

	private static void compressFiles2Zip(String rootName, List<File> files, File targetFile) throws IOException {
		byte[] buffer = new byte[1024 * 64];// 缓存大小为64KB
		try (ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(targetFile);) {
			// Use Zip64 extensions for all entries where they are required
			zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
			// 不压缩
			zipArchiveOutputStream.setLevel(Deflater.NO_COMPRESSION);
			for (File file : files) {

				// 返回相对路径的绝对路径
				String absolutePath = file.getAbsolutePath();

				// 如果开启debug日志 就打印一下当前压缩文件的完整路径
				if (LOGGER.isDebugEnabled())
					LOGGER.debug(absolutePath);

				/*
				 * 如：\root 只取root
				 */
				String tmpPath = absolutePath.substring(absolutePath.indexOf(rootName));

				// 将每个文件用ZipArchiveEntry封装，使用ZipArchiveOutputStream写到压缩文件 排除根路径
				ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(file, tmpPath);
				zipArchiveOutputStream.putArchiveEntry(zipArchiveEntry);

				if (!file.isDirectory()) {
					try (InputStream inputStream = new FileInputStream(file)) {
						int len = -1;
						while ((len = inputStream.read(buffer)) != -1) {
							// 把缓冲区的字节写入到ZipArchiveEntry
							zipArchiveOutputStream.write(buffer, 0, len);
						}
					}
				}
			}
			zipArchiveOutputStream.closeArchiveEntry();
			zipArchiveOutputStream.finish();
		}

	}

	/**
	 * 将zip压缩包解压成文件到指定文件夹
	 * 
	 * @param zipFilePath
	 * @param targetDirPath
	 * @return
	 */
	public boolean decompressZip2Files(String zipFilePath, String targetDirPath) {

		InputStream inputStream = null;
		OutputStream outputStream = null;
		// zip文件输入流
		ZipArchiveInputStream zipArchiveInputStream = null;
		ArchiveEntry archiveEntry = null;
		try {
			File zipFile = new File(zipFilePath);
			inputStream = new FileInputStream(zipFile);
			zipArchiveInputStream = new ZipArchiveInputStream(inputStream, "UTF-8");

			while (null != (archiveEntry = zipArchiveInputStream.getNextEntry())) {
				// 获取文件名
				String archiveEntryFileName = archiveEntry.getName();
				// 构造解压后文件的存放路径
				String archiveEntryPath = targetDirPath + archiveEntryFileName;
				// 把解压出来的文件写到指定路径
				File entryFile = new File(archiveEntryPath);
				if (!entryFile.exists()) {
					entryFile.getParentFile().mkdirs();
				}
				byte[] buffer = new byte[1024 * 5];
				outputStream = new FileOutputStream(entryFile);
				int len = -1;
				while ((len = zipArchiveInputStream.read(buffer)) != -1) {
					outputStream.write(buffer, 0, len);
				}
				outputStream.flush();
			}
		} catch (FileNotFoundException e) {
			// e.printStackTrace();
			return false;
		} catch (IOException e) {
			// e.printStackTrace();
			return false;
		} finally {
			if (null != outputStream) {
				try {
					outputStream.close();
				} catch (IOException e) {
					// e.printStackTrace();
				}
			}
			if (null != zipArchiveInputStream) {
				try {
					zipArchiveInputStream.close();
				} catch (IOException e) {
					// e.printStackTrace();
				}
			}
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					// e.printStackTrace();
				}
			}
		}
		return true;
	}

}
